/*
 * @(#)TemplateDirectiveLoader.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.core.result.template.directive;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * TemplateDirectiveModel을 찾아 Freemarker {@link Configuration} 에 등록.
 *
 * @author Web Platform Development Team
 */
public class TemplateDirectiveLoader {
    Log log = LogFactory.getLog(TemplateDirectiveLoader.class);

    /**
     * Gets the template directives.
     *
     * @param packageName the package name
     * @return the template directives
     * @throws IOException            Signals that an I/O exception has occurred.
     * @throws ClassNotFoundException the class not found exception
     */
    public Map<String, Class<?>> getTemplateDirectives(String packageName) throws IOException, ClassNotFoundException {

        ClassLoader classLoader = this.getClass().getClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();

        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }

        Map<String, Class<?>> templateDirectives = new HashMap<String, Class<?>>();

        for (File directory : dirs) {
            templateDirectives.putAll(findClasses(directory, packageName));
        }

        return templateDirectives;
    }

    /**
     * Find classes.
     *
     * @param directory   the directory
     * @param packageName the package name
     * @return the map< string, class<?>>
     * @throws ClassNotFoundException the class not found exception
     */
    private Map<String, Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class<?>> classesInPackage = new ArrayList<Class<?>>();
        Map<String, Class<?>> templateDirectives = new HashMap<String, Class<?>>();

        if (!directory.exists()) {
            return templateDirectives;
        }

        File[] files = directory.listFiles();

        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    templateDirectives.putAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    classesInPackage.add(Class.forName(packageName + '.'
                            + file.getName().substring(0, file.getName().length() - 6)));
                }
            }
        }
        for (Class<?> ep : classesInPackage) {
            String name = getTemplateDirectiveName(ep);

            if (name != null) {
                templateDirectives.put(name, ep);
            }
        }

        return templateDirectives;
    }

    /**
     * Gets the template directive name.
     *
     * @param clazz the clazz
     * @return the template directive name
     */
    private String getTemplateDirectiveName(Class<?> clazz) {
        Class<?>[] interfaces = clazz.getInterfaces();
        boolean isTemplateDirectiveType = false;

        for (Class<?> ep : interfaces) {
            if (ep.equals(TemplateDirective.class)) {
                isTemplateDirectiveType = true;
            }
        }
        if (!isTemplateDirectiveType) {
            return null;
        }

        TemplateDirective templateDirective = (TemplateDirective)clazz.getAnnotation(TemplateDirective.class);

        if (templateDirective == null) {
            return null;
        }

        String templateDirectiveName = templateDirective.name();

        if (templateDirectiveName == null || templateDirectiveName.length() == 0) {
            log.warn("templateDirectiveName is empty");
            return null;
        }

        return templateDirectiveName;
    }
}
